<?php

namespace System\Libraries;

if (!defined('BASEPATH')) die('Access Denied.');

/**
 * µMVC Framework (c) 2011.
 * 
 * PHP5 MVC Framework software for PHP 5.2.17 or later
 * 
 * @package         µMVC
 * @filename        system/library/validator.class.php
 * @copyright       Lanz (c) 2011.
 * 
 */

class ValidatorException extends \System\Core\CommonException {}

/**
 * Validator is only used by Validation to store a single validation rule.
 */
class Validator
{

    public $variableName = NULL;
    public $value = NULL;
    public $fieldName = NULL;
    public $command = NULL;
    public $commandValue = NULL;
    public $errorMsg = NULL;

    function __construct($variableName, $value, $fieldName, $command, $errorMsg)
    {       
        // Get the validation command and the optional command value.
        $e = explode('=', $command);
        $this->command = $e[0];

        if(isset($e[1]) && !empty($e[1]))
        {
            $this->commandValue = $e[1];
        }

        $this->value = $value;
        
        $this->variableName = $variableName;
        $this->fieldName = $fieldName;
        $this->errorMsg = $errorMsg; 
    }

}

/**
 * Validation Library class
 * 
 * @package         µMVC
 * @subpackage      Library
 * @category        System
 * 
 * The following validations are available:
 * 
 *      std                 can only contain "standard" characters (alnum plus some special chars)
 *      alpha               can only contain alpha chars
 *      alnum               can only contain alphanumeric chars.
 *      email               must contain a valid e-mail address.
 *      equal=<field>       must be equal to <field>.
 *      float               must contain a float value.
 *      int                 must contain an integer value.
 *      minlen=<length>     must be at least <length> long.
 *      maxlen=<lenght>     can not be longer than <length> long.
 *      num                 must contain a numeric value.
 *      req                 is required to be entered.
 *      gte=<number>        the field value must be greater or equal to <number>
 *      lte=<number>        the field value must be lower or equal to <number>
 *      trim                command that will do a trim() on the field.
 */

class Validation
{
    
    /**
     * holds errors generated from a validation.
     * @var type 
     */
    private $_errors = array();
    
    /**
     * holds all validation rules.
     * @var type 
     */
    private $_validators = array();
    
    /**
     * This function adds one validation for a $_POST variable
     * @param string $variableName the name of the $_POST variable.
     * @param string $command The validation command to use
     * @param string $errorMsg  The error message that should be raised if the
     * value is not approved.
     */
    public function addRule($variableName, $fieldName, $commands, $errorMsg = FALSE)
    {
        $value = $this->_getValue($variableName);
        // Create one validator per command.
        $commands = explode('|', $commands);
        foreach ($commands as $command)
        {
            $validator = new Validator($variableName, $value, $fieldName, $command, $errorMsg);
            array_push($this->_validators, $validator);
        }
    }
    
     /**
     * Returns all error messages if any is available.
     * @return array of error messages
     */
    public function getErrors()
    {
        return $this->_errors;
    }

    /**
     * Returns either the URL paired mvc param or the traditional $_POST value.
     * @param string $key name of the param.
     * @return mixed 
     */
    private function _getValue($key)
    {
        $input = \System\Core\Input::instance();
        
        if (!is_null($input->post($key, NULL)))
        {
            return $input->post($key);
        }
        elseif (!is_null($input->param($key, NULL)))
        {
            return $input->param($key);
        }
        else
        {
            throw new ValidatorException('$key is not a post or param variable.');
        }
    }
    
    /**
     * Run the validtaion for all the specified variables.
     * @return boolean TRUE if all validations succeeded.
     */
    public function run()
    {
        $result = TRUE;
               
        foreach ($this->_validators as $val)
        {
            if (!$this->_validateObject($val))
            {
                $result = FALSE;
            }
        }
        
        return $result;
    }
    
    private function _validateObject(Validator $val)
    {
        switch ($val->command)
        {
            case 'std':
                $result = $this->_validateStandardChars($val);
                break;
            
            case 'alpha':
                $result = $this->_validateAlpha($val);
                break;
            
            case 'alnum':
                $result = $this->_validateAlphanum($val);
                break;

            case 'email':
                $result = $this->_validateEmail($val);
                break;
            
            case 'equal':
                $result = $this->_validateEqual($val);
                break;
            
            case 'float':
                $result = $this->_validateFloat($val);
                break;
                       
            case 'int':
                $result = $this->_validateInt($val);
                break;

            case 'minlen':
                $result = $this->_validateMinLength($val);
                break;
            
            case 'maxlen':
                $result = $this->_validateMaxLength($val);
                break;

            case 'num':
                $result = $this->_validateNumeric($val);
                
            case 'req': // Required
                $result = $this->_validateRequired($val);
                break;

            case 'gte': // Greater than or equal to
                $result = $this->_validateGTE($val);
                break;

            case 'lte': // Lower than or equal to
                $result = $this->_validateLTE($val);
                break;
            
            case 'trim': 
                $result = $this->_commandTrim($val);
                break;
            
            default:
                $result = false;
        }
        return $result;
    }

    private function _validateStandardChars(Validator $val)
    {
        if (preg_match('/[^a-zA-Z0-9_-]/i', $val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can only contain the characters a to z, A to Z, 0 to 9 and _ -.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);            
            return FALSE;            
        }
        return TRUE;
    }
    
    private function _validateAlpha(Validator $val)
    {
        if (preg_match('/[^[:alpha:]]/i', $val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can only contain the characters a to z, A to Z.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);            
            return FALSE;            
        }
        return TRUE;
    }

    private function _validateAlphanum(Validator $val)
    {
        if (preg_match('/[^[:alnum:]]/i', $val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can only contain the characters a to z, A to Z and 0 to 9.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);            
            return FALSE;            
        }
        return TRUE;
    }
    
    private function _validateEmail(Validator $val)
    {
        $regexp = '/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/';
        if (!preg_match($regexp, $val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field does not contain a valid e-mail address.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);            
            return FALSE;
        }
        return TRUE;
    }

    private function _validateEqual(Validator $val)
    {
        $equal = $this->_getValue($val->commandValue);

        if ($val->value !== $equal)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field must be equal to the %2$s field.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName, $val->commandValue);            
            return FALSE;
        }
        return TRUE;
    }
    
    private function _validateFloat(Validator $val)
    {
        if (!(is_numeric($val->value) and $val->value != (int)$val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field must contain a valid decimal number.';
            $this->_errors[] = sprintf($val->errorMsg, $val->fieldName);            
            return FALSE;
        }
        return TRUE;
    }
 
    private function _validateInt(Validator $val)
    {
        if (!(is_numeric($val->value) and $val->value == (int)$val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field must contain a valid integer.';
            $this->_errors[] = sprintf($val->errorMsg, $val->fieldName);            
            return FALSE;
        }
        return TRUE;
    }
    
    private function _validateMinLength(Validator $val)
    {
        if (strlen($val->value) < (int)$val->commandValue)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can not be shorter than %2$d characters.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName, $val->commandValue);
            return FALSE;
        }        
        return TRUE;
    }

    private function _validateMaxLength(Validator $val)
    {
        if (strlen($val->value) > (int)$val->commandValue)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can not be longer than %2$d characters.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName, $val->commandValue);
            return FALSE;
        }
        return TRUE;
    }
      
    private function _validateNumeric(Validator $val)
    {
        if (is_numeric($val->value))
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field can only contain a numeric value.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);            
            return FALSE;
        }
        return TRUE;
    }
    
    private function _validateRequired(Validator $val) 
    {
        if (!isset($val->value) || strlen($val->value) <=0)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field is required.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName);
            return FALSE;
        }
        return TRUE;
    }
    
    private function _validateGTE(Validator $val)
    {
        if (!isset($val->value) || $val->value < $val->commandValue)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field must be greater than or equal to %2$d.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName, $val->commandValue);
            return FALSE;
        }
        return TRUE;
    }

    private function _validateLTE(Validator $val)
    {
        if (!isset($val->value) || $val->value > $val->commandValue)
        {
            $errorMsg = $val->errorMsg ? $val->errorMsg : 'The %1$s field must be lower than or equal to %2$d.';
            $this->_errors[] = sprintf($errorMsg, $val->fieldName, $val->commandValue);
            return FALSE;
        }
        return TRUE;
    }
    
    private function _commandTrim(Validator $val)
    {
        if (isset($_POST[$val->variableName]))
        {           
            $_POST[$val->variableName] = trim($val->value);
        }
        else
        {
            throw new ValidatorException('Validator: Can not trim $_POST variable do not exist.');
        }
        
        return TRUE;
    }
    
}
?>
